/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：太平金科-基础组件封装层-SpringBoot组件封装-核心依赖
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taipingframework.boot.web.client;

import cn.hutool.core.date.DatePattern;
import com.taipingframework.utility.constant.SystemConstant;
import com.taipingframework.utility.exception.UtilityException;
import com.taipingframework.utility.extend.ServletHelper;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * webmvc环境下向客户端写入Cookie的实现
 */
public interface ServletCookieScene {

    /**
     * 写入Cookie
     */
    static void writeCookie(BrowserCookie cookie) throws UtilityException {
        try {
            HttpServletRequest request = ServletHelper.getRequest();
            HttpServletResponse response = ServletHelper.getResponse();

            StringBuilder sb = new StringBuilder();
            // 设置Cookie的名称和值
            sb.append(URLEncoder.encode(cookie.getName(), StandardCharsets.UTF_8.name()))
                    .append('=')
                    .append(URLEncoder.encode(cookie.getValue(), StandardCharsets.UTF_8.name()));
            // 设置Cookie的有效期(单位：秒)
            int maxAge = cookie.getCookieMaxAge();
            if (maxAge > SystemConstant.DEFAULT_INVALID) {
                sb.append("; Max-Age=").append(cookie.getCookieMaxAge());
                ZonedDateTime expires = (maxAge != 0)
                        ? ZonedDateTime.now(Clock.systemUTC()).plusSeconds(maxAge)
                        //? ZonedDateTime.now(Clock.systemDefaultZone()).plusSeconds(maxAge)
                        : Instant.EPOCH.atZone(ZoneOffset.UTC);
                sb.append("; Expires=").append(expires.format(DatePattern.NORM_DATETIME_FORMATTER));
            }
            /*/ 设置Cookie所属域名
            String domain = request.getServerName();
            if (domain != null && domain.length() > 0) {
                sb.append("; Domain=").append(domain);
            }*/
            // 设置Cookie(在某个域名下)的存放路径
            String path = request.getContextPath() + "/";
            sb.append("; Path=").append(path);
            // 设置Cookie是否为安全的
            if (request.isSecure()) {
                sb.append("; Secure");
            }
            // 防止客户端脚本通过document.cookie属性访问Cookie，保护Cookie不被跨站脚本攻击窃取或篡改。
            sb.append("; HttpOnly");
            // 防止 CSRF 攻击 和用户追踪（第三方恶意获取cookie），限制第三方 Cookie。
            String sameSite = cookie.getSameSite();
            if (StringUtils.isNotBlank(sameSite)) {
                sb.append("; SameSite=").append(sameSite);
            }

            response.addHeader("Set-Cookie", sb.toString());
        } catch (Exception e) {
            throw new UtilityException(e);
        }
    }

    /**
     * 读取Cookie
     */
    static BrowserCookie readCookie(String cookieName) throws UtilityException {
        try {
            HttpServletRequest request = ServletHelper.getRequest();

            String encodeCookieName = URLEncoder.encode(cookieName, StandardCharsets.UTF_8.name());
            Cookie[] cookies = request.getCookies();
            if (cookies == null || cookies.length <= 0) {
                return new BrowserCookie(encodeCookieName, StringUtils.EMPTY, Duration.ZERO);
            }

            List<BrowserCookie> matchingCookieValues = new ArrayList<>();
            for (Cookie cookie : cookies) {
                if (!encodeCookieName.equals(cookie.getName())) {
                    continue;
                }
                String cookieValue = URLDecoder.decode(cookie.getValue(), StandardCharsets.UTF_8.name());
                if (StringUtils.isBlank(cookieValue)) {
                    continue;
                }

                matchingCookieValues.add(new BrowserCookie(encodeCookieName, cookieValue, Duration.ofSeconds(cookie.getMaxAge())));
            }

            return matchingCookieValues.size() <= 0
                    ? new BrowserCookie(encodeCookieName, StringUtils.EMPTY, Duration.ZERO)
                    : matchingCookieValues.get(0);
        } catch (Exception e) {
            throw new UtilityException(e);
        }
    }

}
